﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Linq;
using System.Collections;
using System.Collections.ObjectModel;

namespace Gmantis.Controls.DataGrid
{
    public enum DataGridSelectionMode
    {
        None,
        SingleCell,
        SingleRow,
        SingleColumn,
        SingleRange,
        MultiRow,
        MultiColumn,
        MultiRange
    }

    internal class DataGridModel
    {
        // Fields
        private DataGrid _dataGrid;
        private DataGridFilterManager _filterManager;
        private DataGridFocusAndEditingManager _focusAndEditingManager;
        private DataGridGroupManager _groupManager;
        private bool _ignoreCurrentCellChanges;
        private DataGridNewRowManager _newRowManager;
        private DataGridSortManager _sortManager;

        // Events
        public event EventHandler ColumnsLoaded;
        public event EventHandler<DataGridColumnEventArgs> ColumnWidthChanged;
        public event EventHandler<PropertyChangedEventArgs<DataGridCell>> CurrentCellChanged;
        public event EventHandler<PropertyChangingEventArgs<DataGridCell>> CurrentCellChanging;
        public event EventHandler<PropertyChangedEventArgs<DataGridCell>> CurrentEditingCellChanged;
        public event EventHandler<PropertyChangedEventArgs<DataGridRow>> CurrentEditingRowChanged;
        public event EventHandler<DataGridRowEventArgs> GroupRowVisibilityChanged;
        public event EventHandler ItemsSourceChanged;
        public event EventHandler<DataGridRowEventArgs> RowDetailsVisibilityChanged;
        public event EventHandler<DataGridRowEventArgs> RowHeightChanged;
        public event EventHandler<DataGridRowsAddedEventArgs> RowsAdded;
        public event EventHandler RowsChanged;
        public event EventHandler RowsCollapsed;
        public event EventHandler<DataGridRowsDeletedEventArgs> RowsDeleted;
        public event EventHandler RowsExpanded;
        public event EventHandler<DataGridRowEventArgs> RowVisibilityChanged;
        public event EventHandler<DataGridSelectionChangedEventArgs> SelectionChanged;

        // Methods
        public DataGridModel(DataGrid dataGrid, DataGridSelectionMode selectionMode)
        {
            EventHandler<DataGridColumnEventArgs> handler = null;
            EventHandler<DataGridColumnEventArgs> handler2 = null;
            EventHandler<DataGridColumnEventArgs> handler3 = null;
            EventHandler<DataGridColumnEventArgs> handler4 = null;
            EventHandler<DataGridRowEventArgs> handler5 = null;
            EventHandler<DataGridRowEventArgs> handler6 = null;
            this._dataGrid = dataGrid;
            this.Columns = new DataGridColumnCollection(this);
            if (handler == null)
            {
                handler = delegate(object s, DataGridColumnEventArgs e)
                {
                    if (this.SelectionManager.Selection.Mode.IsRowSelectionMode())
                    {
                        this.SelectionManager.Selection.Clear(true);
                    }
                };
            }
            this.Columns.BeforeAddColumn += handler;
            if (handler2 == null)
            {
                handler2 = (s, e) => this.PrepareColumn(e.Column);
            }
            this.Columns.AfterAddColumn += handler2;
            if (handler3 == null)
            {
                handler3 = delegate(object s, DataGridColumnEventArgs e)
                {
                    if (this.FocusAndEditingManager.CurrentColumn == e.Column)
                    {
                        this.FocusAndEditingManager.ClearCurrentColumn();
                    }
                    if (this.SelectionManager.SelectedColumns.Contains(e.Column))
                    {
                        this.SelectionManager.Selection.Clear(true);
                    }
                };
            }
            this.Columns.BeforeRemoveColumn += handler3;
            if (handler4 == null)
            {
                handler4 = (s, e) => this.ReleaseColumn(e.Column);
            }
            this.Columns.AfterRemoveColumn += handler4;
            this.Rows = new DataGridRowCollection(this, null);
            if (handler5 == null)
            {
                handler5 = delegate(object s, DataGridRowEventArgs e)
                {
                    this.PrepareRow(e.Row);
                    this.DataGrid.OnLoadedRow(new DataGridRowEventArgs(e.Row));
                };
            }
            this.Rows.AfterRowLoaded += handler5;
            if (handler6 == null)
            {
                handler6 = delegate(object s, DataGridRowEventArgs e)
                {
                    this.DataGrid.OnUnloadingRow(new DataGridRowEventArgs(e.Row));
                    this.ReleaseRow(e.Row);
                };
            }
            this.Rows.BeforeRowUnloaded += handler6;
            this.Initializing = false;
            this._focusAndEditingManager = new DataGridFocusAndEditingManager(this);
            this.SelectionManager = new DataGridSelectionManager(this, selectionMode);
            this._sortManager = new DataGridSortManager(this);
            this._filterManager = new DataGridFilterManager(this);
            this._newRowManager = new DataGridNewRowManager(this);
            this._groupManager = new DataGridGroupManager(this);
            this.DataConnection = new DataConnection(this._dataGrid, this._sortManager, this._filterManager, this._groupManager);
            this.DataConnection.CollectionChanged += new CollectionChangedEventHandler(this.DataConnection_CollectionChanged);
            this.DataConnection.CurrentChanging += new CurrentChangingEventHandler(this.DataConnection_CurrentChanging);
            this.DataConnection.CurrentChanged += new EventHandler(this.DataConnection_CurrentChanged);
        }

        public void AutoGenerateColumns()
        {
            if (!this.Initializing && !this.IsAutoGenerating)
            {
                this.IsAutoGenerating = true;
                this.ClearAutoGeneratedColumns();
                if (this.DataGrid.AutoGenerateColumns && (this.DataGrid.ItemsSource != null))
                {
                    if (this.DataConnection.DataProperties != null)
                    {
                        List<KeyValuePair<int, DataGridColumn>> list = new List<KeyValuePair<int, DataGridColumn>>();
                        foreach (PropertyInfo info in this.DataConnection.DataProperties)
                        {
                            DisplayAttribute attribute = this.GetAttribute<DisplayAttribute>(info);
                            if (((attribute == null) || !attribute.GetAutoGenerateField().HasValue) || attribute.GetAutoGenerateField().Value)
                            {
                                DataGridBoundColumn column;
                                string name = info.PropertyType.GetNonNullableType().Name;
                                if (info.PropertyType.GetNonNullableType().IsNumeric())
                                {
                                    column = new DataGridNumericColumn(info);
                                }
                                else if (name == typeof(bool).Name)
                                {
                                    column = new DataGridCheckBoxColumn(info);
                                }
                                else if ((name == typeof(string).Name) || (name == typeof(char).Name))
                                {
                                    column = new DataGridTextColumn(info);
                                }
                                else if (name == typeof(DateTime).Name)
                                {
                                    column = new DataGridDateTimeColumn(info);
                                }
                                else if (info.PropertyType.GetNonNullableType().IsEnum)
                                {
                                    column = new DataGridComboBoxColumn(info);
                                }
                                else
                                {
                                    column = new DataGridBoundColumn(info);
                                }
                                DataGridAutoGeneratingColumnEventArgs e = new DataGridAutoGeneratingColumnEventArgs(info, column);
                                this.DataGrid.OnAutoGeneratingColumn(e);
                                if (!e.Cancel)
                                {
                                    e.Column.IsAutoGenerated = true;
                                    this.Columns.AddAutogenerated(e.Column);
                                    if ((attribute != null) && attribute.GetOrder().HasValue)
                                    {
                                        list.Add(new KeyValuePair<int, DataGridColumn>(attribute.GetOrder().Value, e.Column));
                                    }
                                }
                            }
                        }
                        foreach (KeyValuePair<int, DataGridColumn> pair in from p in list
                                                                           orderby p.Key
                                                                           select p)
                        {
                            try
                            {
                                this.MoveColumn(pair.Value, Math.Max(0, Math.Min(this.Columns.OrderedColumns.Length - 1, pair.Key)));
                            }
                            catch
                            {
                            }
                        }
                    }
                    else if (this.DataConnection.DataIsPrimitive)
                    {
                        DataGridBoundColumn column2;
                        Type dataType = this.DataConnection.DataType;
                        if (dataType.IsNumeric())
                        {
                            column2 = new DataGridNumericColumn();
                        }
                        else if (dataType.Name == typeof(bool).Name)
                        {
                            column2 = new DataGridCheckBoxColumn();
                        }
                        else if (dataType.Name == typeof(string).Name)
                        {
                            column2 = new DataGridTextColumn();
                        }
                        else if (dataType.Name == typeof(DateTime).Name)
                        {
                            column2 = new DataGridDateTimeColumn();
                        }
                        else
                        {
                            column2 = new DataGridBoundColumn();
                        }
                        column2.Binding = new Binding();
                        DataGridAutoGeneratingColumnEventArgs args2 = new DataGridAutoGeneratingColumnEventArgs(null, column2);
                        this.DataGrid.OnAutoGeneratingColumn(args2);
                        if (!args2.Cancel)
                        {
                            args2.Column.IsAutoGenerated = true;
                            this.Columns.AddAutogenerated(args2.Column);
                        }
                    }
                    this.OnColumnsLoaded();
                    this.DataGrid.OnAutoGeneratedColumns(new EventArgs());
                }
                this.IsAutoGenerating = false;
            }
        }

        public void BeganCellEdit(FrameworkElement editingElement)
        {
            DataGridColumn column = this.FocusAndEditingManager.CurrentEditingCell.Column;
            DataGridBeganEditEventArgs e = new DataGridBeganEditEventArgs(this.FocusAndEditingManager.CurrentEditingCell.Row, column)
            {
                EditingElement = editingElement
            };
            this.DataGrid.OnBeganEdit(e);
        }

        public bool BeginCellEdit()
        {
            if ((!this.DataGrid.IsEditingRowsAllowed || (this.FocusAndEditingManager.CurrentCell == null)) || !this.FocusAndEditingManager.CurrentCell.IsEditable)
            {
                return false;
            }
            if ((this.FocusAndEditingManager.CurrentEditingRow == null) && !this.BeginRowEdit())
            {
                return false;
            }
            DataGridColumn column = this.FocusAndEditingManager.CurrentCell.Column;
            DataGridBeginningEditEventArgs e = new DataGridBeginningEditEventArgs(this.FocusAndEditingManager.CurrentCell.Row, column);
            this.DataGrid.OnBeginningEdit(e);
            if (e.Cancel)
            {
                return false;
            }
            return this._focusAndEditingManager.BeginEdit();
        }

        public void BeginInit()
        {
            this.Initializing = true;
        }

        public bool BeginRowEdit()
        {
            DataGridRow currentRow = this._focusAndEditingManager.CurrentRow;
            if (((currentRow != null) && currentRow.IsEditable) && currentRow.OnBeginningRowEdit())
            {
                this._focusAndEditingManager.BeginEditRow();
                return currentRow.OnBeganRowEdit();
            }
            return false;
        }

        public bool CancelCellEdit(bool forceCancel, bool exitEditingMode)
        {
            DataGridCell currentEditingCell = this.FocusAndEditingManager.CurrentEditingCell;
            if (currentEditingCell != null)
            {
                DataGridColumn column = currentEditingCell.Column;
                DataGridEndingEditEventArgs e = new DataGridEndingEditEventArgs(currentEditingCell.Row, column)
                {
                    EditingElement = this.EditingCellContent
                };
                this.DataGrid.OnCancelingEdit(e);
                if (!e.Cancel || forceCancel)
                {
                    if (this.EditingCellContent != null)
                    {
                        column.CancelCellEdit(this.EditingCellContent, this.EditingCellUneditedValue);
                    }
                    if (exitEditingMode)
                    {
                        this.FocusAndEditingManager.ClearCurrentEditingCell();
                    }
                    return true;
                }
            }
            return false;
        }

        public bool CancelRowEdit(bool forceCancel)
        {
            DataGridRow currentEditingRow = this.FocusAndEditingManager.CurrentEditingRow;
            if (currentEditingRow != null)
            {
                if (this.FocusAndEditingManager.CurrentEditingCell != null)
                {
                    this.FocusAndEditingManager.ClearCurrentEditingCell();
                }
                if (currentEditingRow.OnCancelingRowEdit() || forceCancel)
                {
                    this.FocusAndEditingManager.ClearCurrentEditingRow();
                    return currentEditingRow.OnCanceledRowEdit();
                }
            }
            return false;
        }

        internal void ClearAutoGeneratedColumns()
        {
            this.Columns.ClearAutogeneratedColumns();
            this._sortManager.InvalidateSortedColumns();
            this._filterManager.InvalidateFilteredColumns();
            this._groupManager.InvalidateGroupedColumns();
        }

        internal void ClearCurrentColumn(bool force = false)
        {
            this.CommitRowEdit(force);
            this._focusAndEditingManager.ClearCurrentColumn();
        }

        internal void ClearCurrentRow(bool force = false)
        {
            this.CommitRowEdit(force);
            this._focusAndEditingManager.ClearCurrentRow();
        }

        private void column_WidthChanged(object sender, PropertyChangedEventArgs<DataGridLength> e)
        {
            this.OnColumnWidthChanged(sender as DataGridColumn);
        }

        public bool CommitCellEdit(bool exitEditingMode, bool force = false)
        {
            DataGridCell currentEditingCell = this.FocusAndEditingManager.CurrentEditingCell;
            if (currentEditingCell != null)
            {
                DataGridColumn column = currentEditingCell.Column;
                DataGridRow row = currentEditingCell.Row;
                column.EndEdit(this.EditingCellContent as Control);
                DataGridEndingEditEventArgs e = new DataGridEndingEditEventArgs(row, column)
                {
                    EditingElement = this.EditingCellContent
                };
                this.DataGrid.OnCommittingEdit(e);
                if (!e.Cancel || force)
                {
                    if (exitEditingMode)
                    {
                        this.FocusAndEditingManager.ClearCurrentEditingCell();
                    }
                    this.DataGrid.OnCommittedEdit(new DataGridCellEventArgs(currentEditingCell));
                    return true;
                }
            }
            return false;
        }

        public bool CommitRowEdit(bool force = false)
        {
            DataGridRow currentEditingRow = this.FocusAndEditingManager.CurrentEditingRow;
            if (currentEditingRow != null)
            {
                if (this.FocusAndEditingManager.CurrentEditingCell != null)
                {
                    this.CommitCellEdit(true, force);
                }
                if (currentEditingRow.OnCommitingRowEdit() || force)
                {
                    this.FocusAndEditingManager.ClearCurrentEditingRow();
                    bool flag = currentEditingRow.OnCommittedRowEdit();
                    if (flag)
                    {
                        this.UpdateCurrentRowFromDataSource();
                    }
                    return flag;
                }
            }
            return false;
        }

        internal DataGridGroupRow CreateGroup(object[] parentGroupKeys, DataGridColumn column, CollectionViewGroup viewGroup)
        {
            DataGridGroupRow ownerGroup = (DataGridGroupRow)this.CreateRow(DataGridRowType.Group, viewGroup.Name);
            ownerGroup.Rows = new DataGridRowCollection(this, ownerGroup);
            ownerGroup.Rows.Source = viewGroup.Items;
            ownerGroup.Column = column;
            ownerGroup.GroupKeys = parentGroupKeys.Concat<object>(new object[] { viewGroup.Name }).ToArray<object>();
            ownerGroup.DataItem = viewGroup.Name;
            return ownerGroup;
        }

        internal DataGridRow CreateRow(DataGridRowType type, object dataItem)
        {
            DataGridRow row;
            switch (type)
            {
                case DataGridRowType.New:
                    row = new DataGridNewRow();
                    break;

                case DataGridRowType.Group:
                    row = new DataGridGroupRow();
                    break;

                default:
                    row = new DataGridRow();
                    break;
            }
            DataGridCreatingRowEventArgs e = new DataGridCreatingRowEventArgs(row, type, dataItem);
            this.DataGrid.OnCreatingRow(e);
            return e.Row;
        }

        private void DataConnection_CollectionChanged(object sender, CollectionChangedEventArgs e)
        {
            this.FinalizeEditing(true, true);
            if (e.ItemPropertiesChanged)
            {
                this.AutoGenerateColumns();
            }
            if ((e.AddedItems != null) && (e.AddedItems.Length > 0))
            {
                this.OnRowsAdded(e.AddedItems[0].Key, e.AddedItems[0].Value);
            }
            else if ((e.RemovedItems != null) && (e.RemovedItems.Length > 0))
            {
                this.OnRowsRemoved(e.RemovedItems[0].Key, e.RemovedItems[0].Value);
            }
            else
            {
                this.UpdateModel();
            }
        }

        private void DataConnection_CurrentChanged(object sender, EventArgs e)
        {
            this.UpdateCurrentRowFromDataSource();
        }

        private void DataConnection_CurrentChanging(object sender, CurrentChangingEventArgs e)
        {
            try
            {
                this._ignoreCurrentCellChanges = true;
                this.FinalizeEditing(true, false);
            }
            catch (DataGridFinalizeEditingException)
            {
                if (!e.IsCancelable)
                {
                    this.CancelCellEdit(true, true);
                    this.CancelRowEdit(true);
                }
                else
                {
                    e.Cancel = true;
                }
            }
            finally
            {
                this._ignoreCurrentCellChanges = false;
            }
        }

        public void EndInit()
        {
            this.Initializing = false;
            this.Reload(true);
        }

        public void FilterBy(params DataGridColumnValue<DataGridFilterState>[] filteredColumns)
        {
            this.FilterBy(false, filteredColumns);
        }

        private void FilterBy(bool preservePreviousFilters, params DataGridColumnValue<DataGridFilterState>[] filteredColumns)
        {
            try
            {
                this.FinalizeEditing(true, false);
                this._filterManager.FilterBy(preservePreviousFilters, filteredColumns);
            }
            catch (DataGridFinalizeEditingException)
            {
            }
        }

        public void FilterBy(DataGridColumn column, DataGridFilterState filterState, bool preservePreviousFilters)
        {
            this.FilterBy(preservePreviousFilters, new DataGridColumnValue<DataGridFilterState>[] { new DataGridColumnValue<DataGridFilterState>(column, filterState) });
        }

        private void FinalizeEditing(bool finalizeEditingRow, bool force = false)
        {
            if (((this.FocusAndEditingManager.CurrentEditingCell != null) && !this.CommitCellEdit(true, force)) && !force)
            {
                throw new DataGridFinalizeEditingException("Commit cell edit not allowed!");
            }
            if ((finalizeEditingRow && (this.FocusAndEditingManager.CurrentEditingRow != null)) && (!this.CommitRowEdit(force) && !force))
            {
                throw new DataGridFinalizeEditingException("Commit row edit not allowed!");
            }
        }

        private T GetAttribute<T>(PropertyInfo property)
        {
            if (property != null)
            {
                object[] customAttributes = property.GetCustomAttributes(typeof(T), true);
                if (customAttributes.Length > 0)
                {
                    return (T)customAttributes[0];
                }
            }
            return default(T);
        }

        public DataGridCell GetCell(DataGridRow row, DataGridColumn column)
        {
            return new DataGridCell(row, column);
        }

        public DataGridCell GetCell(int rowIndex, int columnIndex)
        {
            if (((rowIndex < 0) || (rowIndex >= this.Rows.Count)) || ((columnIndex < 0) || (columnIndex >= this.Columns.Count)))
            {
                return null;
            }
            DataGridRow row = this.Rows[rowIndex];
            DataGridColumn column = this.Columns[columnIndex];
            return this.GetCell(row, column);
        }

        public DataGridColumn GetFirstEditableColumn()
        {
            foreach (DataGridColumn column in this.Columns.OrderedColumns)
            {
                if (column.ActualIsEditable)
                {
                    return column;
                }
            }
            return null;
        }

        private DataGridGroupRow GetOrCreateGroup(DataGridRowCollection rows, int localPosition)
        {
            DataGridGroupRow row = null;
            CollectionViewGroup viewGroup = rows.Source[localPosition] as CollectionViewGroup;
            if ((localPosition < rows._groups.Count) && (rows._groups[localPosition].DataItem == viewGroup.Name))
            {
                return rows._groups[localPosition];
            }
            object[] parentGroupKeys = (rows.OwnerGroup != null) ? rows.OwnerGroup.GroupKeys : new object[0];
            if (viewGroup != null)
            {
                string propertyName = this.GroupManager.GroupedFields[parentGroupKeys.Length].PropertyName;
                DataGridColumn column = this.Columns.FirstOrDefault<DataGridColumn>(c => c.ActualSortMemberPath == propertyName);
                row = this.CreateGroup(parentGroupKeys, column, viewGroup);
                rows.InsertGroup(localPosition, row);
            }
            return row;
        }

        public void GroupBy(DataGridColumnValue<DataGridSortDirection>[] columns)
        {
            this.GroupBy(columns, false);
        }

        public void GroupBy(DataGridColumnValue<DataGridSortDirection>[] columns, bool preserveOtherColumnsGrouping)
        {
            try
            {
                this.FinalizeEditing(true, false);
                this._groupManager.GroupBy(columns, preserveOtherColumnsGrouping);
            }
            catch (DataGridFinalizeEditingException)
            {
            }
        }

        public void InsertGroup(DataGridColumn column, int index)
        {
            try
            {
                this.FinalizeEditing(true, false);
                this._groupManager.InsertGroup(column, index);
            }
            catch (DataGridFinalizeEditingException)
            {
            }
        }

        internal bool MoveColumn(DataGridColumn movedColumn, int toColumnDisplayIndex)
        {
            this.FinalizeEditing(false, false);
            DataGridColumnReorderingEventArgs e = new DataGridColumnReorderingEventArgs(movedColumn)
            {
                TargetColumnIndex = toColumnDisplayIndex
            };
            this.DataGrid.OnColumnReordering(e);
            if (!e.Cancel)
            {
                this.SelectionManager.Selection.Clear();
                this.Columns.MoveColumn(movedColumn, toColumnDisplayIndex);
            }
            return !e.Cancel;
        }

        private void OnColumnsLoaded()
        {
            if (this.ColumnsLoaded != null)
            {
                this.ColumnsLoaded(this, new EventArgs());
            }
        }

        private void OnColumnWidthChanged(DataGridColumn column)
        {
            if (this.ColumnWidthChanged != null)
            {
                this.ColumnWidthChanged(this, new DataGridColumnEventArgs(column));
            }
        }

        internal virtual void OnCurrentCellChanged(PropertyChangedEventArgs<DataGridCell> e)
        {
            try
            {
                if (!this._ignoreCurrentCellChanges)
                {
                    this.DataConnection.MoveCurrentToPosition((e.NewValue != null) ? e.NewValue.Row.DataSourceIndex : -1);
                }
            }
            catch
            {
            }
            EventHandler<PropertyChangedEventArgs<DataGridCell>> currentCellChanged = this.CurrentCellChanged;
            if (currentCellChanged != null)
            {
                currentCellChanged(this, e);
            }
        }

        internal virtual void OnCurrentCellChanging(PropertyChangingEventArgs<DataGridCell> e)
        {
            EventHandler<PropertyChangingEventArgs<DataGridCell>> currentCellChanging = this.CurrentCellChanging;
            if (currentCellChanging != null)
            {
                currentCellChanging(this, e);
            }
        }

        internal virtual void OnCurrentEditingCellChanged(PropertyChangedEventArgs<DataGridCell> e)
        {
            EventHandler<PropertyChangedEventArgs<DataGridCell>> currentEditingCellChanged = this.CurrentEditingCellChanged;
            if (currentEditingCellChanged != null)
            {
                currentEditingCellChanged(this, e);
            }
        }

        internal virtual void OnCurrentEditingRowChanged(PropertyChangedEventArgs<DataGridRow> e)
        {
            if (e.OldValue != null)
            {
                e.OldValue.IsEditing = false;
            }
            if (e.NewValue != null)
            {
                e.NewValue.IsEditing = true;
            }
            EventHandler<PropertyChangedEventArgs<DataGridRow>> currentEditingRowChanged = this.CurrentEditingRowChanged;
            if (currentEditingRowChanged != null)
            {
                currentEditingRowChanged(this, e);
            }
        }

        internal void OnGroupRowVisibilityChanged(DataGridGroupRow group)
        {
            EventHandler<DataGridRowEventArgs> groupRowVisibilityChanged = this.GroupRowVisibilityChanged;
            if (groupRowVisibilityChanged != null)
            {
                groupRowVisibilityChanged(this, new DataGridRowEventArgs(group));
            }
        }

        internal void OnItemsSourceChanged(IEnumerable itemsSource)
        {
            this.FinalizeEditing(true, true);
            this.DataConnection.DataSource = itemsSource;
            this.Reload(true);
            this.UpdateCanUserFilter();
            if (this.ItemsSourceChanged != null)
            {
                this.ItemsSourceChanged(this, new EventArgs());
            }
        }

        private void OnRowHeightChanged(DataGridRow row)
        {
            if (this.RowHeightChanged != null)
            {
                this.RowHeightChanged(this, new DataGridRowEventArgs(row));
            }
        }

        private void OnRowsAdded(DataGridRow[] addedRows)
        {
            if (this.RowsAdded != null)
            {
                this.RowsAdded(this, new DataGridRowsAddedEventArgs(addedRows));
            }
        }

        private void OnRowsAdded(int addedDataSourceIndex, object addedItem)
        {
            DataGridRow row = null;
            if (this.GroupManager.GroupCount == 0)
            {
                row = this.Rows.InsertRow(addedDataSourceIndex, addedItem);
            }
            else if (addedDataSourceIndex == 0)
            {
                DataGridGroupRow orCreateGroup = null;
                DataGridRowCollection rows = this.Rows;
                for (int i = 0; i < this.GroupManager.GroupCount; i++)
                {
                    orCreateGroup = this.GetOrCreateGroup(rows, 0);
                    if (orCreateGroup != null)
                    {
                        rows = orCreateGroup.Rows;
                    }
                    else
                    {
                        return;
                    }
                }
                row = this.TryInsert(orCreateGroup.Rows, addedItem, 0, null);
            }
            else
            {
                DataGridRow rowFromDataSourceIndex = this.Rows.GetRowFromDataSourceIndex(addedDataSourceIndex - 1);
                if (rowFromDataSourceIndex != null)
                {
                    row = this.TryInsert(rowFromDataSourceIndex.ParentCollection, addedItem, rowFromDataSourceIndex.LocalIndex + 1, null);
                }
                else
                {
                    return;
                }
            }
            if (row != null)
            {
                DataGridColumn firstVisibleColumn = this.Columns.GetFirstVisibleColumn();
                if ((this.FocusAndEditingManager.CurrentColumn == null) && (firstVisibleColumn != null))
                {
                    this.SetCurrentColumn(firstVisibleColumn, false);
                }
                DataGridRow[] addedRows = new DataGridRow[] { row };
                this.OnRowsAdded(addedRows);
                this.DataGrid.OnRowsAdded(new DataGridRowsAddedEventArgs(addedRows));
            }
        }

        private void OnRowsChanged()
        {
            if (this.RowsChanged != null)
            {
                this.RowsChanged(this, new EventArgs());
            }
        }

        internal virtual void OnRowsCollapsed(EventArgs e)
        {
            EventHandler rowsCollapsed = this.RowsCollapsed;
            if (rowsCollapsed != null)
            {
                rowsCollapsed(this, e);
            }
        }

        private void OnRowsDeleted(DataGridRow[] deletedRows)
        {
            if (this.RowsDeleted != null)
            {
                this.RowsDeleted(this, new DataGridRowsDeletedEventArgs(deletedRows));
            }
        }

        internal virtual void OnRowsExpanded(EventArgs e)
        {
            EventHandler rowsExpanded = this.RowsExpanded;
            if (rowsExpanded != null)
            {
                rowsExpanded(this, e);
            }
        }

        private void OnRowsRemoved(int deletedDataSourceIndex, object deletedItem)
        {
            this._ignoreCurrentCellChanges = true;
            DataGridRow rowFromDataSourceIndex = this.Rows.GetRowFromDataSourceIndex(deletedDataSourceIndex);
            if (rowFromDataSourceIndex != null)
            {
                DataGridRow[] removedRows = new DataGridRow[] { rowFromDataSourceIndex };
                DataGridRowCollection parentCollection = rowFromDataSourceIndex.ParentCollection;
                parentCollection.RemoveRows(removedRows);
                this.OnRowsDeleted(removedRows);
                this.DataGrid.OnRowsDeleted(new DataGridRowsDeletedEventArgs(removedRows));
                while (parentCollection.OwnerGroup != null)
                {
                    DataGridGroupRow ownerGroup = parentCollection.OwnerGroup;
                    if (parentCollection.Source.Count != 0)
                    {
                        break;
                    }
                    ownerGroup.ParentCollection.RemoveGroup(ownerGroup);
                }
            }
            this._ignoreCurrentCellChanges = false;
        }

        private void OnRowsReseted(bool preserveTopAndBottomRows, GroupedField[] groupedFields, ReadOnlyObservableCollection<object> groups)
        {
            int selectedIndex = this.DataGrid.SelectedIndex;
            this.Rows.Load(preserveTopAndBottomRows, groupedFields, groups);
            DataGridColumn firstVisibleColumn = this.Columns.GetFirstVisibleColumn();
            if ((this.FocusAndEditingManager.CurrentColumn == null) && (firstVisibleColumn != null))
            {
                this.SetCurrentColumn(firstVisibleColumn, false);
            }
            if ((this.SelectionManager.SelectedRows.Count == 0) && !this.DataGrid.UpdateSelectedItem())
            {
                this.DataGrid.UpdateSelectedIndex(selectedIndex);
            }
            DataGridRow firstVisibleRow = this.Rows.GetFirstVisibleRow(false);
            if ((this.FocusAndEditingManager.CurrentRow == null) && (firstVisibleRow != null))
            {
                if (this.SelectionManager.SelectedRow != null)
                {
                    this.SetCurrentRow(this.SelectionManager.SelectedRow, false);
                }
                else
                {
                    this.SetCurrentRow(firstVisibleRow, false);
                }
            }
            this.OnRowsChanged();
        }

        internal void OnRowVisibilityChanged(DataGridRow row)
        {
            EventHandler<DataGridRowEventArgs> rowVisibilityChanged = this.RowVisibilityChanged;
            if (rowVisibilityChanged != null)
            {
                rowVisibilityChanged(this, new DataGridRowEventArgs(row));
            }
        }

        internal virtual void OnSelectionChanged(DataGridSelectionChangedEventArgs e)
        {
            if (this.DataGrid.RowDetailsVisibilityMode == DataGridRowDetailsVisibilityMode.VisibleWhenSelected)
            {
                foreach (DataGridRow row in this.Rows.GetLoadedRows())
                {
                    if ((row.Type == DataGridRowType.Item) && (row.DataGrid != null))
                    {
                        row.DetailsVisibility = row.IsSelected ? Visibility.Visible : Visibility.Collapsed;
                    }
                }
            }
            this.DataGrid.OnSelectionChanged(e);
            EventHandler<DataGridSelectionChangedEventArgs> selectionChanged = this.SelectionChanged;
            if (selectionChanged != null)
            {
                selectionChanged(this, e);
            }
        }

        internal virtual void OnSelectionChanging(DataGridSelectionChangedEventArgs e)
        {
            this.DataGrid.OnSelectionChanging(e);
        }

        private void PrepareColumn(DataGridColumn column)
        {
            if (!column.IsPrepared)
            {
                column.DataGrid = this.DataGrid;
                column.UpdateActualMinWidth();
                column.UpdateActualMaxWidth();
                column.UpdateActualCanUserFilter();
                column.UpdateActualCanUserResize();
                column.UpdateActualCanUserMove();
                column.WidthChanged += new EventHandler<PropertyChangedEventArgs<DataGridLength>>(this.column_WidthChanged);
                column.IsPrepared = true;
            }
        }

        private void PrepareRow(DataGridRow row)
        {
            row.HeightChanged += new EventHandler<PropertyChangedEventArgs<DataGridLength>>(this.row_HeightChanged);
            row.DetailsVisibilityChanged += new EventHandler<PropertyChangedEventArgs<Visibility>>(this.row_DetailsVisibilityChanged);
            row.VisibilityChanged += new EventHandler<PropertyChangedEventArgs<Visibility>>(this.row_VisibilityChanged);
        }

        private void ReleaseColumn(DataGridColumn column)
        {
            column.WidthChanged -= new EventHandler<PropertyChangedEventArgs<DataGridLength>>(this.column_WidthChanged);
            column.DataGrid = null;
            column.IsPrepared = false;
            if (column.GroupState.Direction != DataGridSortDirection.None)
            {
                foreach (DataGridGroupRow row in from row in this.Rows.GetLoadedRows()
                                                 where row is DataGridGroupRow
                                                 select row)
                {
                    if (row.Column == column)
                    {
                        row.Column = null;
                    }
                }
                this.GroupManager.InvalidateGroupedColumns();
            }
        }

        private void ReleaseRow(DataGridRow row)
        {
            row.HeightChanged -= new EventHandler<PropertyChangedEventArgs<DataGridLength>>(this.row_HeightChanged);
            row.DetailsVisibilityChanged -= new EventHandler<PropertyChangedEventArgs<Visibility>>(this.row_DetailsVisibilityChanged);
            row.VisibilityChanged -= new EventHandler<PropertyChangedEventArgs<Visibility>>(this.row_VisibilityChanged);
            if (this.FocusAndEditingManager.CurrentRow == row)
            {
                this.FocusAndEditingManager.ClearCurrentRow();
            }
            this.SelectionManager.ReleaseRow(row);
        }

        public void Reload(bool createColumns)
        {
            if (!this.Initializing)
            {
                this.Rows.Clear(true);
                if (createColumns)
                {
                    this.AutoGenerateColumns();
                }
                if (this.DataConnection.DataSource != null)
                {
                    DataGridColumnValue<DataGridSortDirection>[] valueArray = (from column in this.Columns
                                                                               where column.InitialSortState != null
                                                                               select new DataGridColumnValue<DataGridSortDirection>(column, column.InitialSortState.Direction)).ToArray<DataGridColumnValue<DataGridSortDirection>>();
                    DataGridColumnValue<DataGridSortDirection>[] valueArray2 = (from column in this.Columns
                                                                                where column.InitialGroupState != null
                                                                                select new DataGridColumnValue<DataGridSortDirection>(column, column.InitialGroupState.Direction)).ToArray<DataGridColumnValue<DataGridSortDirection>>();
                    if ((valueArray.Length > 0) || (valueArray2.Length > 0))
                    {
                        foreach (DataGridColumnValue<DataGridSortDirection> value2 in valueArray)
                        {
                            value2.Column.InitialSortState = null;
                        }
                        foreach (DataGridColumnValue<DataGridSortDirection> value3 in valueArray2)
                        {
                            value3.Column.InitialGroupState = null;
                        }
                        this.DataConnection.BeginUpdate();
                        if (valueArray.Length > 0)
                        {
                            this.DataConnection.SortDescriptions = valueArray;
                        }
                        if (valueArray2.Length > 0)
                        {
                            this.DataConnection.GroupedFields = (from g in valueArray2 select new GroupedField { PropertyName = g.Column.ActualSortMemberPath, Direction = g.Value, GroupConverter = g.Column.GroupConverter }).ToArray<GroupedField>();
                        }
                        this.DataConnection.EndUpdate();
                    }
                    else
                    {
                        this.UpdateModel();
                    }
                }
            }
        }

        public void RemoveGroup(DataGridColumn column)
        {
            try
            {
                this.FinalizeEditing(true, false);
                this._groupManager.RemoveGroup(column);
            }
            catch (DataGridFinalizeEditingException)
            {
            }
        }

        public bool RemoveRows(DataGridRow[] removedRows)
        {
            removedRows = (from row in removedRows
                           where row.DataSourceIndex >= 0
                           select row).ToArray<DataGridRow>();
            if (this.DataGrid.IsRemovingRowsAllowed && (removedRows.Length > 0))
            {
                DataGridDeletingRowsEventArgs e = new DataGridDeletingRowsEventArgs(removedRows);
                this.DataGrid.OnDeletingRows(e);
                if (!e.Cancel)
                {
                    this.SelectionManager.Clear();
                    this.DataConnection.RemoveDataItems((from row in removedRows select row.DataSourceIndex).ToArray<int>());
                    return true;
                }
            }
            return false;
        }

        private void row_DetailsVisibilityChanged(object sender, PropertyChangedEventArgs<Visibility> e)
        {
            if (this.RowDetailsVisibilityChanged != null)
            {
                this.RowDetailsVisibilityChanged(this, new DataGridRowEventArgs((DataGridRow)sender));
            }
        }

        private void row_HeightChanged(object sender, PropertyChangedEventArgs<DataGridLength> e)
        {
            this.OnRowHeightChanged(sender as DataGridRow);
        }

        private void row_VisibilityChanged(object sender, PropertyChangedEventArgs<Visibility> e)
        {
            this.OnRowVisibilityChanged(sender as DataGridRow);
        }

        public bool SetCurrentCell(DataGridCell cell)
        {
            if (cell == null)
            {
                throw new ArgumentNullException("cell");
            }
            if (!(cell != this.FocusAndEditingManager.CurrentCell) || !cell.IsSelectable)
            {
                return true;
            }
            object dataItem = cell.Row.DataItem;
            DataGridRow row = cell.Row;
            this.FinalizeEditing(cell.Row != this.FocusAndEditingManager.CurrentEditingRow, false);
            if (row.DataGrid == null)
            {
                row = this.DataGrid.Rows[dataItem];
            }
            return (((cell.Column.DataGrid != null) && (row != null)) && this._focusAndEditingManager.SetCurrentCell(this.DataGrid[row, cell.Column]));
        }

        public bool SetCurrentColumn(DataGridColumn column, bool force = false)
        {
            this.FinalizeEditing(false, force);
            return this._focusAndEditingManager.SetCurrentColumn(column);
        }

        public bool SetCurrentRow(DataGridRow row, bool force = false)
        {
            try
            {
                object dataItem = row.DataItem;
                this.FinalizeEditing(true, force);
                if (row.DataGrid == null)
                {
                    row = this.DataGrid.Rows[dataItem];
                }
                return (((row != null) && row.IsSelectable) && this._focusAndEditingManager.SetCurrentRow(row));
            }
            catch (DataGridFinalizeEditingException)
            {
                return false;
            }
        }

        public void SortBy(params DataGridColumnValue<DataGridSortDirection>[] columns)
        {
            this.SortBy(false, columns);
        }

        private void SortBy(bool preservePreviousSorting, params DataGridColumnValue<DataGridSortDirection>[] columns)
        {
            try
            {
                this.FinalizeEditing(true, false);
                this._sortManager.SortBy(preservePreviousSorting, columns);
            }
            catch (DataGridFinalizeEditingException)
            {
            }
        }

        public void SortBy(DataGridColumn column, DataGridSortDirection direction, bool preservePreviousSorting)
        {
            this.SortBy(preservePreviousSorting, new DataGridColumnValue<DataGridSortDirection>[] { new DataGridColumnValue<DataGridSortDirection>(column, direction) });
        }

        private DataGridRow TryInsert(DataGridRowCollection rows, object dataitem, int localPosition, Func<CollectionViewGroup, object> calc)
        {
            Func<CollectionViewGroup, object> func = null;
            if (((calc == null) && (localPosition < rows.Source.Count)) && (rows.Source[localPosition] == dataitem))
            {
                return rows.InsertRow(localPosition, dataitem);
            }
            if ((calc != null) && (localPosition < rows.Source.Count))
            {
                if (!(rows.Source[localPosition] is CollectionViewGroup))
                {
                    return rows.InsertRow(0, rows.Source[localPosition]);
                }
                if (calc(rows.Source[localPosition] as CollectionViewGroup) == dataitem)
                {
                    return this.GetOrCreateGroup(rows, localPosition);
                }
            }
            else if (rows.OwnerGroup != null)
            {
                if (func == null)
                {
                    func = delegate(CollectionViewGroup groupItem)
                    {
                        if (calc != null)
                        {
                            return calc(groupItem.Items[0] as CollectionViewGroup);
                        }
                        return groupItem.Items[0];
                    };
                }
                DataGridGroupRow row = this.TryInsert(rows.OwnerGroup.ParentCollection, dataitem, rows.OwnerGroup.LocalIndex + 1, func) as DataGridGroupRow;
                if (row == null)
                {
                    return null;
                }
                if (calc != null)
                {
                    return this.GetOrCreateGroup(row.Rows, 0);
                }
                return row.Rows.InsertRow(0, dataitem);
            }
            return null;
        }

        public void UpdateCanUserFilter()
        {
            foreach (DataGridColumn column in this.Columns)
            {
                column.UpdateActualCanUserFilter();
            }
        }

        public void UpdateCanUserReorderColumns()
        {
            foreach (DataGridColumn column in this.Columns)
            {
                column.UpdateActualCanUserMove();
            }
        }

        public void UpdateCanUserResizeColumns()
        {
            foreach (DataGridColumn column in this.Columns)
            {
                column.UpdateActualCanUserResize();
            }
        }

        public void UpdateCanUserResizeRows()
        {
            foreach (DataGridRow row in this.Rows.GetLoadedRows())
            {
                row.UpdateCanUserResize();
            }
        }

        public void UpdateColumnWidth(DataGridLength oldValue)
        {
            foreach (DataGridColumn column in this.Columns)
            {
                column.UpdateColumnWidth(oldValue);
            }
        }

        private void UpdateCurrentRowFromDataSource()
        {
            this._ignoreCurrentCellChanges = true;
            DataGridRow rowFromDataSourceIndex = this.Rows.GetRowFromDataSourceIndex(this.DataConnection.CurrentPosition);
            if (((rowFromDataSourceIndex != null) && (rowFromDataSourceIndex != this.FocusAndEditingManager.CurrentRow)) && this.SetCurrentRow(rowFromDataSourceIndex, false))
            {
                DataGridSelection selection = this.SelectionManager.Selection;
                selection.BeginUpdate();
                selection.Clear();
                if (selection.Mode.IsCellSelectionMode())
                {
                    DataGridCell currentCell = this.FocusAndEditingManager.CurrentCell;
                    if (currentCell != null)
                    {
                        selection.Add(currentCell, currentCell);
                    }
                }
                else if (selection.Mode.IsRowSelectionMode())
                {
                    selection.Add(rowFromDataSourceIndex, rowFromDataSourceIndex);
                }
                selection.EndUpdate();
            }
            this._ignoreCurrentCellChanges = false;
        }

        public void UpdateGroupRowHeight(DataGridLength oldValue)
        {
            foreach (DataGridRow row in this.Rows.GetLoadedRows())
            {
                if (row.Type == DataGridRowType.Group)
                {
                    row.UpdateRowHeight(oldValue);
                }
            }
        }

        public void UpdateMaxColumnWidth(double oldValue)
        {
            foreach (DataGridColumn column in this.Columns)
            {
                column.UpdateActualMaxWidth();
            }
        }

        public void UpdateMaxRowHeight()
        {
            foreach (DataGridRow row in this.Rows.GetLoadedRows())
            {
                row.UpdateActualMaxHeight();
            }
        }

        public void UpdateMinColumnWidth(double oldValue)
        {
            foreach (DataGridColumn column in this.Columns)
            {
                column.UpdateActualMinWidth();
            }
        }

        public void UpdateMinRowHeight()
        {
            foreach (DataGridRow row in this.Rows.GetLoadedRows())
            {
                row.UpdateActualMinHeight();
            }
        }

        private void UpdateModel()
        {
            DataGridColumnValue<DataGridSortDirection>[] sortDescriptions = this.DataConnection.SortDescriptions;
            GroupedField[] groupedFields = this.DataConnection.GroupedFields;
            DataGridColumnValue<DataGridFilterState>[] filterDescriptions = this.DataConnection.FilterDescriptions;
            this.OnRowsReseted(true, groupedFields, this.DataConnection.Groups);
            this.SortManager.SetCurrentSortedColumns(sortDescriptions);
            this.GroupManager.SetCurrentGroupedColumns(groupedFields);
            this.FilterManager.SetCurrentFilteredColumns(filterDescriptions);
            this.NewRowManager.UpdateNewRowVisibility();
        }

        public void UpdateRowHeight(DataGridLength oldValue)
        {
            foreach (DataGridRow row in this.Rows.GetLoadedRows())
            {
                if (row.Type != DataGridRowType.Group)
                {
                    row.UpdateRowHeight(oldValue);
                }
            }
        }

        // Properties
        public DataGridColumnCollection Columns { get; private set; }

        public DataConnection DataConnection { get; private set; }

        public DataGrid DataGrid
        {
            get
            {
                return this._dataGrid;
            }
        }

        public FrameworkElement EditingCellContent { get; set; }

        public object EditingCellUneditedValue { get; set; }

        public DataGridFilterManager FilterManager
        {
            get
            {
                return this._filterManager;
            }
        }

        public DataGridFocusAndEditingManager FocusAndEditingManager
        {
            get
            {
                return this._focusAndEditingManager;
            }
        }

        public int FrozenBottomRowsCount
        {
            get
            {
                return Math.Min(this.DataGrid.FrozenBottomRowsCount, this.Rows.Count - this.FrozenTopRowsCount);
            }
        }

        public int FrozenColumnCount
        {
            get
            {
                return Math.Max(0, Math.Min(this.DataGrid.FrozenColumnCount, this.Columns.OrderedColumns.Length - 1));
            }
        }

        public int FrozenTopRowsCount
        {
            get
            {
                return Math.Min(this.DataGrid.FrozenTopRowsCount, this.Rows.Count);
            }
        }

        public DataGridGroupManager GroupManager
        {
            get
            {
                return this._groupManager;
            }
        }

        private bool Initializing { get; set; }

        public bool IsAutoGenerating { get; set; }

        public DataGridNewRowManager NewRowManager
        {
            get
            {
                return this._newRowManager;
            }
        }

        public DataGridRowCollection Rows { get; private set; }

        public DataGridSelectionManager SelectionManager { get; private set; }

        public DataGridSortManager SortManager
        {
            get
            {
                return this._sortManager;
            }
        }

        public double VerticalFreezingSeparatorWidth
        {
            get
            {
                if (this.DataGrid.ShowLeftFreezingSeparator)
                {
                    return this.DataGrid.VerticalFreezingSeparatorWidth;
                }
                return 0.0;
            }
        }
    }
}
